function initilize() {
    init_options();
    init_map_services();
    handle_request();
}

function handle_request() {
    chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
        console.log(request);

        if (request.action.startsWith('option')) {
            if (request.action == 'option.write') {
                write_option(request.key, request.value);
            } else if (request.action == 'option.delete') {
                delete_option(request.key);
            }

            sendResponse({
                key: request.key,
                value: read_option(request.key)
            });
        } else if ('oauth' == request.action) {
            douban_oauth(function (access_token) {
                sendResponse({ access_token: access_token });
            });
        } else if ('borrow' == request.action || 'search' == request.action || 'open' == request.action) {
            chrome.tabs.create({
                url: request.url
            }, function (tab) {
                sendResponse({ tab: tab });
            });
        } else if ('order' == request.action) {
            chrome.tabs.create({
                url: 'http://www.qingfanqie.com/Member/User_Liang.aspx'
            }, function (tab) {
                chrome.tabs.executeScript(tab.id, {
                    file: '/assets/lib/jquery/jquery.js'
                }, function () {
                    code = "$('#txtTitle').val('" + request.info.name + "');" +
                        "$('#txtisbn').val('" + request.info.isbn + "');" +
                        "$('#txtanthor').val('" + request.info.author + "');" +
                        "$('#txtconcern').val('" + request.info.publisher + "');";

                    chrome.tabs.executeScript(tab.id, {
                        code: code
                    }, function () {
                    });
                });

                sendResponse({ tab: tab });
            });
        } else if ('load' == request.action) {
            extract_doulist_info(request.info, function (doulist_info) {
                sendResponse({ info: doulist_info });
            });
        } else if ('import' == request.action) {
            var port = chrome.tabs.connect(sender.tab.id, {name: 'importing'});

            console.log("connect to tab #" + sender.tab.id + ' on port ' + port.name);

            import_doulist(port, request.info, function () {
                sendResponse({});
            }, function (error, status, xhr) {
                sendResponse({
                    status: status,
                    error: error
                });
            });
        } else if ('event_sync' == request.action) {
            sync_douban_events(function () {
                sendResponse({});
            }, function (error, status, xhr) {
                sendResponse({
                    status: status,
                    error: error
                });
            });
        } else if ('event' == request.action) {
            get_event_info(request.id, function (info) {
                sendResponse({
                    info: info
                });
            }, function (error, status, xhr) {
                sendResponse({
                    status: status,
                    error: error
                });
            });
        } else if ('location' == request.action) {
            query_location(request.location, function (geometry, status) {
                sendResponse({
                    geometry: geometry,
                    status: status
                });
            })
        } else if ('place' == request.action) {
            query_location(request.location, function (geometry, status) {
                if (geometry) {
                    query_place(geometry.bounds, request.place, function (geometry, status) {
                        sendResponse({
                            geometry: geometry,
                            status: status
                        });
                    });
                } else {
                    sendResponse({
                        geometry: geometry,
                        status: status
                    });
                }
            });
        } else if ('unittests' == request.action) {
            chrome.tabs.create({
                url: chrome.extension.getURL("pages/tests.html")
            }, function (tab) {
                sendResponse({ tab: tab });
            });
        }
    });
}

function fetch_doulist(url, items, callback) {
    console.log("fetch doulist " + url + "?start=" + items.length);

    $.get(url, {start: items.length}, function (html) {
        var doulist = $(html).find('tr.doulist_item');

        if (doulist.length == 0) { callback(items); return; }

        doulist.each(function (idx, obj) {
            var item = $(obj);

            var poster = $(item.find('td a[href]')[0]);
            var item_info = DOUBAN.parse_url(poster.attr('href'));

            item_info.name = item.find('td div a[href]').text();
            item_info.poster = $(poster.find('img')).attr('src');

            items.push(item_info);
        });

        fetch_doulist(url, items, callback);
    });
}

function extract_doulist_info(doulist_info, callback) {
    var url = DOUBAN.join_url(doulist_info);

    fetch_doulist(url, [], function (items) {
        doulist_info.items = items;

        callback(doulist_info);
    });
}

function import_doulist(port, doulist_info, callback, error_callback) {
    var api = DOUBAN.create_api();

    api.addCollection(port, doulist_info, callback, error_callback);
}

function douban_oauth(callback) {
    var api_key = read_option('douban_api_key');
    var api_key_secret = read_option('douban_api_key_secret');

    var api = new DOUBAN.DoubanApi(api_key, api_key_secret);

    api.getRequestToken(function (request_token, request_token_secret) {
        var url = api.getUserAuthorizationURL(request_token);

        chrome.tabs.create({
            url: url
        }, function (tab) {
            console.log("open tab #" + tab.id + " for the douban OAUTH");

            var onUpdated = function (id, info, updatedTab) {
                if (id == tab.id) {
                    if (info.url && !info.url.startsWith('http://www.douban.com/service/auth/authorize'))
                    {
                        console.log("finish the douban OAUTH tab #" + id);

                        chrome.tabs.onUpdated.removeListener(onUpdated);
                        chrome.tabs.onRemoved.removeListener(onRemoved);

                        if (info.url == 'http://www.douban.com/accounts/apptokens') {
                            api.getAccessToken(request_token, request_token_secret,
                                function (access_token, access_token_secret) {
                                    console.log("got a access token with secret");

                                    write_option('douban_access_token', access_token);
                                    write_option('douban_access_token_secret', access_token_secret);

                                    callback(access_token);
                                }, function (xhr, status, error) {
                                    console.log("fail to fetch the access token, reason = " + status + ", error = " + error);

                                    delete_option('douban_access_token');
                                    delete_option('douban_access_token_secret');

                                    callback(null);
                                });
                        } else {
                            callback(null);
                        }
                    }
                }
            };

            var onRemoved = function(id, info, removedTab) {
                if (id == tab.id) {
                    console.log("close the douban OAUTH tab #" + tab.id);

                    chrome.tabs.onUpdated.removeListener(onUpdated);
                    chrome.tabs.onRemoved.removeListener(onRemoved);

                    callback(null);
                }
            };

            chrome.tabs.onUpdated.addListener(onUpdated);
            chrome.tabs.onRemoved.addListener(onRemoved);
        });
    });
}

function sync_douban_events(callback, error_callback) {
    var api = DOUBAN.create_api();

    api.getCurrentUser(function (user) {
        api.getUserEvents(user.uid, 'participate', function (events) {
            callback();
        }, error_callback);
    }, error_callback);
}

function get_event_info(id, callback, error_callback) {
    var api = DOUBAN.create_api();

    api.getEvent(id, callback, error_callback);
}

var GOOGLE_MAP_API_KEY = "AIzaSyBQ79rITSjp_6vWJTF3ALVgBid1_YScjPQ";

var geocoder = null;
var places = null;

var locations = {};
var places = {};

function create_map_services() {
    console.log("loaded the Google Map API");

    geocoder = new google.maps.Geocoder();
    places = new google.maps.places.PlacesService(document.getElementById("map_canvas"));
}

function init_map_services() {
    var url = "https://maps.google.com/maps/api/js?v=3&key=" + GOOGLE_MAP_API_KEY +
              "&callback=create_map_services&libraries=places&sensor=true&language=zh-CN&region=CN";

    $.getScript(url, function () {
        console.log("loading the Google Map API");
    }).fail(function () {
        console.log("fail to load the Google Map API");
    });
}

function query_location(location, callback) {
    if (locations[location]) {
        callback(locations[location]);
    } else {
        geocoder.geocode({ address: location }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                locations[location] = results[0].geometry;

                callback(locations[location], status);
            } else {
                console.log("fail to query location " + location + ", " + status);

                callback(null, status);
            }
        });
    }
}

function query_place(bounds, place, callback) {
    if (places[place]) {
        callback(places[place]);
    } else {
        places.search({
            bounds: bounds,
            name: place
        }, function (results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                console.log("found " + results.length + " matching places for " + place);
                console.log(results);

                places[place] = results[0].geometry;

                callback(places[place], status);
            } else {
                console.log("fail to query place " + place + ", " + status);

                callback(null, status);
            }
        });
    }
}

initilize();
